package cn.lili.modules.payment.kit.plugin.wechat;

import cn.hutool.json.JSONUtil;
import cn.lili.common.enums.ResultCode;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.utils.SpringContextUtil;
import cn.lili.modules.system.entity.dos.Setting;
import cn.lili.modules.system.entity.dto.payment.WechatPaymentSetting;
import cn.lili.modules.system.entity.enums.SettingEnum;
import cn.lili.modules.system.service.SettingService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jfinal.json.FastJson;
import com.jfinal.kit.Ret;
import com.wechat.pay.contrib.apache.httpclient.WechatPayHttpClientBuilder;
import com.wechat.pay.contrib.apache.httpclient.WechatPayUploadHttpPost;
import com.wechat.pay.contrib.apache.httpclient.auth.PrivateKeySigner;
import com.wechat.pay.contrib.apache.httpclient.auth.Verifier;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Credentials;
import com.wechat.pay.contrib.apache.httpclient.auth.WechatPay2Validator;
import com.wechat.pay.contrib.apache.httpclient.cert.CertificatesManager;
import com.wechat.pay.contrib.apache.httpclient.exception.HttpCodeException;
import com.wechat.pay.contrib.apache.httpclient.exception.NotFoundException;
import com.wechat.pay.contrib.apache.httpclient.util.PemUtil;
import com.wechat.pay.contrib.apache.httpclient.util.RsaCryptoUtil;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.Consts;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.*;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
import org.springframework.data.redis.core.StringRedisTemplate;

import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException;
import java.io.*;
import java.net.URI;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.GeneralSecurityException;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;

@SuppressWarnings("ALL")
@NoArgsConstructor
@Slf4j
public class WechatV3Api {

    public static JSONObject getAreaProvinces() throws NotFoundException, IOException {
        CloseableHttpClient httpClient = getHttpClient();

        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/capital/capitallhh/areas/provinces");
        httpGet.addHeader("Accept", "*/*");

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return JSONObject.parseObject(bodyAsString);
        }
    }

    public static JSONObject getCities(String provinceCode) throws NotFoundException, IOException {
        CloseableHttpClient httpClient = getHttpClient();

        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/capital/capitallhh/areas/provinces/" + provinceCode + "/cities");
        httpGet.addHeader("Accept", "*/*");

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return JSONObject.parseObject(bodyAsString);
        }
    }

    public static JSONObject getBranches(String bankAliasCode, String cityCode) throws NotFoundException, IOException {
        CloseableHttpClient httpClient = getHttpClient();

        List<BasicNameValuePair> list = new ArrayList<>();
        list.add(new BasicNameValuePair("city_code", cityCode));
        list.add(new BasicNameValuePair("offset", "0"));
        list.add(new BasicNameValuePair("limit", "200"));
        String params = EntityUtils.toString(new UrlEncodedFormEntity(list, Consts.UTF_8));
        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/" + bankAliasCode + "/branches?" + params);
        httpGet.addHeader("Accept", "*/*");
        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return JSONObject.parseObject(bodyAsString);
        }
    }

    public static JSONObject searchBank(String accountNumber) throws IOException, NotFoundException {
        CloseableHttpClient httpClient = getHttpClient();
        WechatPaymentSetting setting = getSetting();
        Date date = new Date();
        List<BasicNameValuePair> list = new ArrayList<>();

        list.add(new BasicNameValuePair("account_number", encrypt(accountNumber)));
        String params = EntityUtils.toString(new UrlEncodedFormEntity(list, Consts.UTF_8));
        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/capital/capitallhh/banks/search-banks-by-bank-account?" + params);
        httpGet.addHeader("Accept", "*/*");
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        Verifier verifier = certificatesManager.getVerifier(setting.getMchId());
        httpGet.addHeader("Wechatpay-Serial", verifier.getValidCertificate().getSerialNumber().toString(16));

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return JSONObject.parseObject(bodyAsString);
        }
    }

    private static Ret buildResult(String bodyAsString) {
        Ret ret = FastJson.getJson().parse(bodyAsString, Ret.class);
        String code = ret.getStr("code");
        if (StringUtils.isEmpty(code)) {
            ret.setOk();
        } else {
            log.error(bodyAsString);
            ret.setFail().set("msg", ret.get("message"));
        }
        return ret;
    }

    @SneakyThrows
    public static Ret upload(File file) {
        CloseableHttpClient httpClient = getHttpClient();
        URI uri = new URI("https://api.mch.weixin.qq.com/v3/merchant/media/upload");
        try (FileInputStream ins1 = new FileInputStream(file)) {
            String sha256 = DigestUtils.sha256Hex(ins1);
            try (InputStream ins2 = new FileInputStream(file)) {
                HttpPost httpPost = new WechatPayUploadHttpPost.Builder(uri)
                        .withImage(file.getName(), sha256, ins2)
                        .build();

                try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
                    String bodyAsString = EntityUtils.toString(response.getEntity());
                    return buildResult(bodyAsString);
                }
            }
        }
    }

    @SneakyThrows
    public static Ret upload(String url) {
        String ext = FilenameUtils.getExtension(url);
        File desk = new File(FileUtils.getTempDirectory(), UUID.randomUUID().toString() + "." + ext);
        FileUtils.copyURLToFile(new URL(url), desk);
        Ret ret = upload(desk);
        FileUtils.deleteQuietly(desk);
        return ret;
    }

    @SneakyThrows
    public static Ret applyments(Ret param) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/applyments/");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");
        WechatPaymentSetting setting = getSetting();

        JSONObject idCardInfo = param.getAs("id_card_info");
        idCardInfo.put("id_card_name", encrypt(idCardInfo.getString("id_card_name")));
        idCardInfo.put("id_card_number", encrypt(idCardInfo.getString("id_card_number")));
        if (StringUtils.isNotEmpty(idCardInfo.getString("id_card_address"))) {
            idCardInfo.put("id_card_address", encrypt(idCardInfo.getString("id_card_address")));
        }
//        JSONObject idDocInfo = param.getAs("id_doc_info");
//        idDocInfo.put("id_doc_name", encrypt(idDocInfo.getString("id_doc_name")));
//        idDocInfo.put("id_doc_number", encrypt(idDocInfo.getString("id_doc_number")));
//        if (StringUtils.isNotEmpty(idDocInfo.getString("id_doc_address"))) {
//            idDocInfo.put("id_doc_address", encrypt(idDocInfo.getString("id_doc_address")));
//        }

        JSONObject accountInfo = param.getAs("account_info");
        accountInfo.put("account_name", encrypt(accountInfo.getString("account_name")));
        accountInfo.put("account_number", encrypt(accountInfo.getString("account_number")));

        JSONObject contactInfo = param.getAs("contact_info");
        contactInfo.put("contact_name", encrypt(contactInfo.getString("contact_name")));
        if (StringUtils.isNotEmpty(contactInfo.getString("contact_id_card_number"))) {
            contactInfo.put("contact_id_card_number", encrypt(contactInfo.getString("contact_id_card_number")));
        }
        contactInfo.put("mobile_phone", encrypt(contactInfo.getString("mobile_phone")));
        contactInfo.put("contact_email", encrypt(contactInfo.getString("contact_email")));

        String bankAccountType = accountInfo.getString("bank_account_type");
        JSONArray banks = "74".equalsIgnoreCase(bankAccountType) ? getCorporateBanks() : getPersonalBanks();
        banks.stream().filter(o -> {
            JSONObject obj = (JSONObject) o;
            return StringUtils.equals(obj.getString("bank_alias"), accountInfo.getString("account_bank"));
        }).findFirst().ifPresent(o -> {
            accountInfo.put("account_bank", ((JSONObject) o).getString("account_bank"));
        });

        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        Verifier verifier = certificatesManager.getVerifier(setting.getMchId());
        httpPost.addHeader("Wechatpay-Serial", verifier.getValidCertificate().getSerialNumber().toString(16));
        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(param.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    public static Ret getApplyment(String applymentId, String outRequestNo) throws IOException, NotFoundException {
        CloseableHttpClient httpClient = getHttpClient();

        String url = null;
        if (StringUtils.isNotEmpty(applymentId)) {
            url = "https://api.mch.weixin.qq.com/v3/ecommerce/applyments/" + applymentId;
        } else if (StringUtils.isNotEmpty(outRequestNo)) {
            url = "https://api.mch.weixin.qq.com/v3/ecommerce/applyments/out-request-no/" + outRequestNo;
        }
        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader("Accept", "*/*");

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret sPayH5(boolean isCombine, Ret param) {
        CloseableHttpClient httpClient = getHttpClient();
        String url;
        if (isCombine) {
            url = "https://api.mch.weixin.qq.com/v3/combine-transactions/h5";
        } else {
            url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/h5";
        }
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(param.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        log.info("sPayH5下单：{} {}", param, isCombine);
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret sPayJs(boolean isCombine, Ret param) {
        CloseableHttpClient httpClient = getHttpClient();
        String url;
        if (isCombine) {
            url = "https://api.mch.weixin.qq.com/v3/combine-transactions/jsapi";
        } else {
            url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/jsapi";
        }
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(param.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret sPayNative(boolean isCombine, Ret param) {
        CloseableHttpClient httpClient = getHttpClient();
        String url;
        if (isCombine) {
            url = "https://api.mch.weixin.qq.com/v3/combine-transactions/native";
        } else {
            url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/native";
        }
        HttpPost httpPost = new HttpPost(url);
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(param.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));

        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret sQueryPayOrder(boolean isCombine, String transactionId, String outTradeNo) {
        String url = "";
        if (isCombine) {
            url = "https://api.mch.weixin.qq.com/v3/combine-transactions/out-trade-no/" + outTradeNo;
        } else {
            if (StringUtils.isNotEmpty(transactionId)) {
                url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/id/" + transactionId;
            } else {
                url = "https://api.mch.weixin.qq.com/v3/pay/partner/transactions/out-trade-no/" + outTradeNo;
            }
        }
        CloseableHttpClient httpClient = getHttpClient();
        WechatPaymentSetting setting = getSetting();
        HttpGet httpGet = new HttpGet(url);
        httpGet.addHeader("Accept", "*/*");
        //CertificatesManager certificatesManager = CertificatesManager.getInstance();
        //Verifier verifier = certificatesManager.getVerifier(setting.getMchId());
        //httpGet.addHeader("Wechatpay-Serial", verifier.getValidCertificate().getSerialNumber().toString(16));

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret createSubsidies(String transactionId, String subMchId, String outSubsidyNo, int amount, String description, String refundId) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/subsidies/create");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(Ret.create("sub_mchid", subMchId).toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret returnSubsidies(String transactionId, String subMchId, String outSubsidyNo, int amount, String description, String refundId) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/subsidies/return");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(Ret.create("sub_mchid", subMchId).toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret returnAdvance(String refundId, String subMchId) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/refunds/" + refundId + "/return-advance");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(Ret.create("sub_mchid", subMchId).toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret refundApply(Ret param) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/refunds/apply");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(param.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret refundApply(String refundId, String subMchId) {
        CloseableHttpClient httpClient = getHttpClient();
        WechatPaymentSetting setting = getSetting();
        List<BasicNameValuePair> list = new ArrayList<>();
        list.add(new BasicNameValuePair("sub_mchid", subMchId));
        String params = EntityUtils.toString(new UrlEncodedFormEntity(list, Consts.UTF_8));
        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/ecommerce/refunds/id/" + refundId + "?" + params);
        httpGet.addHeader("Accept", "*/*");
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        Verifier verifier = certificatesManager.getVerifier(setting.getMchId());
        httpGet.addHeader("Wechatpay-Serial", verifier.getValidCertificate().getSerialNumber().toString(16));

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret refundApplyByOutNo(String outRefundNo, String subMchId) {
        CloseableHttpClient httpClient = getHttpClient();
        WechatPaymentSetting setting = getSetting();
        List<BasicNameValuePair> list = new ArrayList<>();
        list.add(new BasicNameValuePair("sub_mchid", subMchId));
        String params = EntityUtils.toString(new UrlEncodedFormEntity(list, Consts.UTF_8));
        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/ecommerce/refunds/out-refund-no/" + outRefundNo + "?" + params);
        httpGet.addHeader("Accept", "*/*");
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        Verifier verifier = certificatesManager.getVerifier(setting.getMchId());
        httpGet.addHeader("Wechatpay-Serial", verifier.getValidCertificate().getSerialNumber().toString(16));

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret getComplaintDetail(String complaintId) {
        CloseableHttpClient httpClient = getHttpClient();
        WechatPaymentSetting setting = getSetting();
        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/" + complaintId);
        httpGet.addHeader("Accept", "*/*");
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        Verifier verifier = certificatesManager.getVerifier(setting.getMchId());

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret getComplaintNotifications() {
        CloseableHttpClient httpClient = getHttpClient();
        WechatPaymentSetting setting = getSetting();
        HttpGet httpGet = new HttpGet("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications");
        httpGet.addHeader("Accept", "*/*");
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        Verifier verifier = certificatesManager.getVerifier(setting.getMchId());

        try (CloseableHttpResponse response = httpClient.execute(httpGet)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret addComplaintNotifications(String url) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(Ret.create("url", url).toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }


    @SneakyThrows
    public static Ret deleteComplaintNotifications() {
        CloseableHttpClient httpClient = getHttpClient();
        WechatPaymentSetting setting = getSetting();
        HttpDelete httpDelete = new HttpDelete("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications");
        httpDelete.addHeader("Accept", "*/*");
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        Verifier verifier = certificatesManager.getVerifier(setting.getMchId());

        try (CloseableHttpResponse response = httpClient.execute(httpDelete)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret updateComplaintNotifications(String url) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPut httpPut = new HttpPut("https://api.mch.weixin.qq.com/v3/merchant-service/complaint-notifications");
        httpPut.addHeader("Accept", "application/json");
        httpPut.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(Ret.create("url", url).toJson(), bos, "utf-8");

        httpPut.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPut)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret response(String complaintId, String complainted_mchid) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/" + complaintId + "/response");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Ret param = Ret.create().set("complainted_mchid", complainted_mchid).set("response_content", "已与用户沟通解决");

        IOUtils.write(param.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret updateRefundProgress(String complaintId, String action) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/" + complaintId + "/update-refund-progress");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        Ret param = Ret.create().set("action", action).set("remark", "已处理完成");
        if ("APPROVE".equals(action)) {
            param.set("launch_refund_day", 0);
        }
        IOUtils.write(param.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret completeRefund(String complaintId, String complainted_mchid) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/merchant-service/complaints-v2/" + complaintId + "/complete");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(Ret.create("complainted_mchid", complainted_mchid).toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret addReceivers(String appId, String type, String account, String name, String relationType) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/profitsharing/receivers/add");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        Ret params = Ret.create("appid", appId)
                .set("type", type)
                .set("account", account)
                .set("relation_type", relationType);
        if (StringUtils.isNotEmpty(name)) {
            params.set("name", name);
        }
        IOUtils.write(params.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret deleteReceivers(String appId, String type, String account) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/profitsharing/receivers/delete");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        Ret params = Ret.create("appid", appId)
                .set("type", type)
                .set("account", account);

        IOUtils.write(params.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }


    @SneakyThrows
    public static Ret profitSharing(Ret params) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/profitsharing/orders");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(params.toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    @SneakyThrows
    public static Ret completeProfitsharing(String subMchId, String transId, String outOrderNo, String description) {
        CloseableHttpClient httpClient = getHttpClient();
        HttpPost httpPost = new HttpPost("https://api.mch.weixin.qq.com/v3/ecommerce/profitsharing/finish-order");
        httpPost.addHeader("Accept", "application/json");
        httpPost.addHeader("Content-type", "application/json; charset=utf-8");

        ByteArrayOutputStream bos = new ByteArrayOutputStream();

        IOUtils.write(Ret.create()
                .set("sub_mchid", subMchId)
                .set("transaction_id", transId)
                .set("out_order_no", outOrderNo)
                .set("description", description)
                .toJson(), bos, "utf-8");

        httpPost.setEntity(new StringEntity(bos.toString("UTF-8"), "UTF-8"));
        try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
            String bodyAsString = EntityUtils.toString(response.getEntity());
            return buildResult(bodyAsString);
        }
    }

    public static JSONArray getPersonalBanks() throws IOException, NotFoundException {
        StringRedisTemplate stringRedisTemplate = getStringRedisTemplate();
        return JSONObject.parseArray(StringUtils.defaultString(stringRedisTemplate.opsForValue().get("wechat:v3:PersonalBanks"), "[]"));

    }

    @SneakyThrows
    static void initPersonalBanks() {
        if (CollectionUtils.isNotEmpty(getPersonalBanks())) {
            return;
        }
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        String domain = "https://api.mch.weixin.qq.com";
        String url = "/v3/capital/capitallhh/banks/personal-banking?offset=0&limit=200";
        JSONArray data = new JSONArray();
        while (true) {
            HttpGet httpGet = new HttpGet(domain + url);
            httpGet.addHeader("Accept", "*/*");

            response = httpClient.execute(httpGet);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject obj = JSONObject.parseObject(bodyAsString);
            JSONObject links = obj.getJSONObject("links");
            String next = links.getString("next");
            data.addAll(obj.getJSONArray("data"));
            if (StringUtils.isEmpty(next)) {
                break;
            } else {
                url = next;
            }
        }
        StringRedisTemplate stringRedisTemplate = getStringRedisTemplate();
        stringRedisTemplate.opsForValue().set("wechat:v3:PersonalBanks", JSON.toJSONString(data));
        if (response != null) {
            response.close();
        }
    }

    public static JSONArray getCorporateBanks() throws IOException, NotFoundException {
        StringRedisTemplate stringRedisTemplate = getStringRedisTemplate();
        return JSONObject.parseArray(StringUtils.defaultString(stringRedisTemplate.opsForValue().get("wechat:v3:CorporateBanks"), "[]"));

    }

    @SneakyThrows
    static void initCorporateBanks() {
        if (CollectionUtils.isNotEmpty(getCorporateBanks())) {
            return;
        }
        CloseableHttpClient httpClient = getHttpClient();
        CloseableHttpResponse response = null;
        String domain = "https://api.mch.weixin.qq.com";
        String url = "/v3/capital/capitallhh/banks/corporate-banking?offset=0&limit=200";
        JSONArray data = new JSONArray();
        while (true) {
            HttpGet httpGet = new HttpGet(domain + url);
            httpGet.addHeader("Accept", "*/*");

            response = httpClient.execute(httpGet);
            String bodyAsString = EntityUtils.toString(response.getEntity());
            JSONObject obj = JSONObject.parseObject(bodyAsString);
            JSONObject links = obj.getJSONObject("links");
            String next = links.getString("next");
            data.addAll(obj.getJSONArray("data"));
            if (StringUtils.isEmpty(next)) {
                break;
            } else {
                url = next;
            }
        }
        StringRedisTemplate stringRedisTemplate = getStringRedisTemplate();
        stringRedisTemplate.opsForValue().set("wechat:v3:CorporateBanks", JSON.toJSONString(data));
        if (response != null) {
            response.close();
        }
    }

    @SneakyThrows
    public static void init() throws HttpCodeException, GeneralSecurityException, IOException {
        WechatPaymentSetting setting = getSetting();
        String merchantId = setting.getMchId();
        String merchantSerialNumber = setting.getSerialNumber();
        String apiV3Key = setting.getApiKey3();

        PrivateKey pk = getPrivateKey();
        // 获取证书管理器实例
        CertificatesManager certificatesManager = CertificatesManager.getInstance();

        // 向证书管理器增加需要自动更新平台证书的商户信息
        certificatesManager.putMerchant(merchantId, new WechatPay2Credentials(merchantId,
                new PrivateKeySigner(merchantSerialNumber, pk)), apiV3Key.getBytes(StandardCharsets.UTF_8));
        initPersonalBanks();
        initCorporateBanks();
    }

    public static PrivateKey getPrivateKey() throws FileNotFoundException {
        WechatPaymentSetting setting = getSetting();
        return PemUtil.loadPrivateKey(new FileInputStream(new File(setting.getApiclient_key())));
    }

    public static WechatPaymentSetting getSetting() {
        WechatPaymentSetting setting;
        try {
            SettingService settingService = (SettingService) SpringContextUtil.getBean("settingServiceImpl");
            Setting systemSetting = settingService.get(SettingEnum.WECHAT_PAYMENT.name());
            setting = JSONUtil.toBean(systemSetting.getSettingValue(), WechatPaymentSetting.class);
        } catch (Exception e) {
            log.error("获取微信配置异常", e);
            throw new ServiceException(ResultCode.ERROR);
        }
        return setting;
    }

    static StringRedisTemplate getStringRedisTemplate() {
        return (StringRedisTemplate) SpringContextUtil.getBean("stringRedisTemplate");
    }

    static CloseableHttpClient getHttpClient() throws NotFoundException {
        PrivateKey pk = null;
        try {
            pk = getPrivateKey();
        } catch (FileNotFoundException e) {
            log.error("未找到请求私钥", e);
            return null;
        }
        CertificatesManager certificatesManager = CertificatesManager.getInstance();
        WechatPaymentSetting setting = getSetting();
        String merchantId = setting.getMchId();
        String merchantSerialNumber = setting.getSerialNumber();
        // ... 若有多个商户号，可继续调用putMerchant添加商户信息
        // 从证书管理器中获取verifier
        WechatPayHttpClientBuilder builder = WechatPayHttpClientBuilder.create()
                .withMerchant(merchantId, merchantSerialNumber, pk)
                .withValidator(new WechatPay2Validator(certificatesManager.getVerifier(merchantId)));
        // 通过WechatPayHttpClientBuilder构造的HttpClient，会自动的处理签名和验签，并进行证书自动更新

        // ... 接下来，你仍然可以通过builder设置各种参数，来配置你的HttpClient
        return builder.build();
    }

    public static String encrypt(String content) {
        try {
            WechatPaymentSetting setting = getSetting();
            String merchantId = setting.getMchId();
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            Verifier verifier = certificatesManager.getVerifier(merchantId);
            X509Certificate validCertificate = verifier.getValidCertificate();
            return RsaCryptoUtil.encryptOAEP(content, validCertificate);
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    public static String decrypt(String content) {
        try {
            WechatPaymentSetting setting = getSetting();
            String merchantId = setting.getMchId();
            CertificatesManager certificatesManager = CertificatesManager.getInstance();
            Verifier verifier = certificatesManager.getVerifier(merchantId);
            X509Certificate validCertificate = verifier.getValidCertificate();

            return RsaCryptoUtil.decryptOAEP(content, getPrivateKey());
        } catch (BadPaddingException e) {
            e.printStackTrace();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (NotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

}
